from pymodbus import Framer
from pymodbus.client import ModbusSerialClient as BaseModbusSerialClient
from datetime import datetime
from pymodbus.pdu import ModbusRequest
from service.common.message_queue import MessageQueue
from service.log import log


class ModbusRtuClient(BaseModbusSerialClient):
    def __init__(self, port: str, baudrate: int = 9600, parity: str = 'N', stopbits: int = 1, bytesize: int = 8,
                 timeout: float = 1.0):
        super().__init__(
            method='rtu',
            port=port,
            baudrate=baudrate,
            timeout=timeout,
            parity=parity,
            stopbits=stopbits,
            bytesize=bytesize,
            framer=Framer.RTU
        )
        self.message_queue = MessageQueue(max_size=1000)

    def execute(self, request: ModbusRequest):
        # RTU帧结构：地址 + 功能码 + 数据 + CRC校验[10](@ref)
        request_frame = bytes([request.slave_id]) + bytes([request.function_code]) + request.encode()
        crc = self._calculate_crc(request_frame)  # 计算CRC校验码
        full_request = request_frame + crc

        # 记录发送日志
        request_hex = " ".join(f"{b:02x}" for b in full_request)
        send_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
        send_str = f"[{send_time}] RTU TX: {request_hex}"
        log.info(send_str)
        self.message_queue.enqueue(send_str)

        # 执行请求
        response = super().execute(request)

        # 处理响应
        if response:
            response_frame = bytes([response.slave_id]) + bytes([response.function_code]) + response.encode()
            response_crc = self._calculate_crc(response_frame)
            full_response = response_frame + response_crc
            response_hex = " ".join(f"{b:02x}" for b in full_response)
            receive_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
            response_str = f"[{receive_time}] RTU RX: {response_hex}"
            log.info(response_str)
            self.message_queue.enqueue(response_str)
        return response

    def _calculate_crc(self, data: bytes) -> bytes:
        """计算RTU帧的CRC校验码（低字节在前）[10](@ref)"""
        crc = 0xFFFF
        for byte in data:
            crc ^= byte
            for _ in range(8):
                if crc & 0x0001:
                    crc = (crc >> 1) ^ 0xA001
                else:
                    crc >>= 1
        return bytes([crc & 0xFF, (crc >> 8) & 0xFF])

    def get_latest_message(self) -> list:
        """获取最新的消息"""
        message_list = []
        while not self.message_queue.is_empty():
            message_list.append(self.message_queue.dequeue())
        return message_list
